package Datenbank;

import java.sql.*;
import java.util.*;
import Applikationslogik.*;

/**
 * Bildet Objekte des Typs Termin auf der relationalen Datenbank ab.
 * Die Klasse beinhaltet Methoden und Objekte zum Anlegen, Ändern, Löschen und Suchen.
 *
 * @author Daniel Mossiossian & René van Straaten
 */
public class TerminMapper {
        private static TerminMapper terminMapper = null;

    /**
     * Stellt sicher, dass nur eine Istanz von TerminMapper erzeugt wird.
     *
     * @return terminMapper das terminMapper- Objekt
     */
    public static TerminMapper TerminMapper() {
        if (terminMapper == null) {
            terminMapper = new TerminMapper();
        }

        return terminMapper;
    }

    /**
    * Fügt ein neues Termin- Objekt in die Datenbank ein.
    *
    * @param te das das zu speichernde Termin- Objekt
    * @return te das bereits übergebene Objekt
    */
    public void insert(Termin te) {
        Connection con = DBConnection.connection();

        try {
            Statement stmt = con.createStatement();

            
            ResultSet rs = stmt.executeQuery(
                    "SELECT MAX(eintrag_id) AS maxid " +
                    "FROM Kalendereintrag");


            if (rs.next()) {
                try {
                    // Hier wird die bisher höhste vorhandene eintrag_id um 1 erhöht.
                     
                    te.set_eintrag_id(rs.getInt("maxid") + 1);

                    stmt = con.createStatement();

                    // Die tatsächliche Einfügeoperation mit den übergebenden Attributen.
                     

                    stmt.executeUpdate("INSERT INTO Kalendereintrag (eintrag_id, typ, erinnerung, ort, start, ende, beschreibung, wiederholung, kategorie_id, person_id, serienmuster, anzahl) VALUES (" +
                            te.get_eintrag_id() + ",'" +
                            "t" + "'," +
                            te.get_erinnerung() + ",'" +
                            te.get_ort() + "','" +
                            te.get_start() + "','" +
                            te.get_ende() + "','" +
                            te.get_beschreibung() + "'," +
                            te.get_wiederholung_id() + "," +
                            te.get_kategorie() + "," +
                            te.get_erstelltvon() + ",'" +
                            te.get_serienmuster() + "'," +
                            te.get_anzahl() + ")");

                    stmt.executeUpdate("INSERT INTO Teilnahme (kalender_id, eintrag_id, teilnahme) VALUES (" +
                            te.get_kalender_id() + "," +
                            te.get_eintrag_id() + "," +
                            "1" + ")");


                    for(int i=0; i < te.get_eingeladene_personen().size(); i++)
                    {
                        stmt.executeUpdate("INSERT INTO Teilnahme (eintrag_id, teilnahme, person_id) VALUES (" +
                            te.get_eintrag_id() + "," +
                            0 +  "," + 
                            te.get_eingeladene_personen().get(i).get_person_id() + ")");
                    }


                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
        } catch (SQLException e2) {
            e2.printStackTrace();
        }           
    }

    /**
     * Aktualisiert das übergebene Termin- Objekt in der Datenbank.
     *
     * @param te das zu aktualisierende Termin- Objekt
     * @return te das als Parameter übergebene Objekt des Typs Termin
     */
    public Termin update(Termin te) {
        // Stellt durch Aufruf der connection() Methode der DBConnection-Klasse, die Verbindung zur Datenbank her.
        Connection con = DBConnection.connection();

        try {
            // Leeres SQL-Statement stmt wird angelegt.
            Statement stmt = con.createStatement();

                    // Der Termin wird hier mit den neuen Daten überschrieben, diese wurden davor in der Verwaltung überprüft
                   stmt.executeUpdate("UPDATE Kalendereintrag " +
                    "SET eintrag_id=" + te.get_eintrag_id() + ", " +
                    "erinnerung=" + te.get_erinnerung() + ", " +
                    "ort='" + te.get_ort() + "', " +
                    "start='" + te.get_start() + "', " +
                    "ende='" + te.get_ende() + "', " +
                    "beschreibung='" + te.get_beschreibung() + "', " +
                    // "enddatum='null', " +
                    "kategorie_id=" + te.get_kategorie() + " " +
                    "WHERE eintrag_id=" + te.get_eintrag_id());

                   // Nun werden noch alle zugewiesenen Teilnehmer entfernt und neu eingeladen // Der Besitzer nimmt immer teil und wird deswegen NICHT gelöscht
                   stmt.executeUpdate("DELETE FROM Teilnahme " +
                    "WHERE eintrag_id=" + te.get_eintrag_id() + " AND person_id!=" + te.get_erstelltvon());

                   // Nun werden alle eingeladenen Teilnehmer Tabelle Teilnehmer geschrieben, diese müssen zuvor aus dem Vector ausgelesen werden
                    for(int i=0; i < te.get_eingeladene_personen().size(); i++)
                    {
                        stmt.executeUpdate("INSERT INTO Teilnahme (eintrag_id, teilnahme, person_id) VALUES (" +
                            te.get_eintrag_id() + "," +
                            0 +  "," + // Teilnahme wird auf 0 gesetzt, da die Teilnehmer den Eintrag noch ihrem Kalender hinzufügen müssen
                            te.get_eingeladene_personen().get(i).get_person_id() + ")");
                    }

        } catch (SQLException e2) {
            e2.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        // das als Parameter übergebende Objekt des Typs Termin wird zurückgegeben
        return te;
    }

    /**
     * Gibt die niedrigste Wiederholung anhand der ID zurück.
     * 
     * @param wiederholung die Wiederholung vom Typ int
     * @return result
     */
    public int get_lowest_eintragid_by_wh(int wiederholung) {
        Connection con = DBConnection.connection();
        int result = 0;
        try {
            Statement stmt = con.createStatement();
            ResultSet rs = stmt.executeQuery(
                    "SELECT MIN(eintrag_id) AS minid " +
                    "FROM Kalendereintrag WHERE wiederholung=" + wiederholung);
            if (rs.next()) {
                try {
                    
                    result = (rs.getInt("minid"));
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
        } catch (SQLException e2) {
            e2.printStackTrace();
        }

        return result;
    }

    /**
     * Gibt die höchste Wiederholung zurück.
     *
     * @return highest_id die höchste Wiederholung vom Typ int
     */
    public int get_highest_wh_id() {
        Connection con = DBConnection.connection();
        int highest_id = 0;
        try {
            Statement stmt = con.createStatement();

          
            ResultSet rs = stmt.executeQuery(
                    "SELECT MAX(wiederholung) AS maxid " +
                    "FROM Kalendereintrag");


            if (rs.next()) {
                try {
                    
                    highest_id = (rs.getInt("maxid"));


                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
        } catch (SQLException e2) {
            e2.printStackTrace();
        }
        return highest_id;
    }

    /**
     * Löscht alle Einträge, die die übergebene Wiederholungs ID besitzen.
     *
     * @param wiederholung die ID der Wiederholung vom Typ int
     */
    public void delete_by_wiederholung(int wiederholung) {
        Connection con = DBConnection.connection();

        try {
            // Leeres SQL-Statement stmt wird angelegt.
            Statement stmt = con.createStatement();

            // Löschen in der Tabelle Teilnahme verknüpft mit INNERJOIN auf Kalendereintrag Tabelle
            stmt.executeUpdate("DELETE Teilnahme.* FROM Teilnahme " +
                    "INNER JOIN Kalendereintrag ON Teilnahme.eintrag_id = Kalendereintrag.eintrag_id " +
                    "WHERE wiederholung=" + wiederholung);

            // Löschen in der Tabelle Kalendereintrag alle Einträge die die übergebene Wiederholungs ID besitzen
            stmt.executeUpdate("DELETE FROM Kalendereintrag " +
                    "WHERE wiederholung=" + wiederholung);



        } catch (SQLException e2) {
            e2.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Findet Wiederholung angand der übergebenen ID.
     *
     * @param id die ID der Wiederholung
     * @return 
     */
    public Wiederholung findbyid(int id) {
        Connection con = DBConnection.connection();

        try {
            Statement stmt = con.createStatement();

            ResultSet rs = stmt.executeQuery(
                    "SELECT * FROM Kalendereintrag " +
                    "WHERE eintrag_id =" + id);

            /*
             * Da es sich bei id um einen Primärschlüssel handelt, kann max. nur ein Tupel zurückgegeben werden.
             * Es wird geprüft, ob ein Ergebnis vorliegt. Wenn ja werden die Attribute eintrag_id, erinnerung,
             * ort, start, ende, beschreibung, wiederholung, kategorie_id, person_id,
             * serienmuster und anzahl ausgelesen und als Typ Termin zurückgegeben.
             */
            if (rs.next()) {
                try {
                    System.out.println(id);
                    WiederholungImpl wh = new WiederholungImpl();
                    wh.set_eintrag_id(rs.getInt("eintrag_id"));
                    wh.set_erinnerung(rs.getInt("erinnerung"));
                    wh.set_ort(rs.getString("ort"));
                    wh.set_start(rs.getTimestamp("start"));
                    wh.set_ende(rs.getTimestamp("ende"));
                    wh.set_beschreibung(rs.getString("beschreibung"));
                    wh.set_wiederholung_id(rs.getInt("wiederholung"));
                    wh.set_kategorie(rs.getInt("kategorie_id"));
                    wh.set_erstelltvon(rs.getInt("person_id"));
                    wh.set_serienmuster(rs.getString("serienmuster").charAt(0));
                    wh.set_anzahl(rs.getInt("anzahl"));


                    try {
                        Statement stmt2 = con.createStatement();

                        ResultSet rs2 = stmt2.executeQuery(
                                "SELECT kalender_id FROM Teilnahme " +
                                "WHERE eintrag_id =" + wh.get_eintrag_id());                    


                        while (rs2.next()) {
                            try {
                                wh.set_kalender_id(rs2.getInt("kalender_id"));
                                return wh;
                            } catch (Exception e) {
                                e.printStackTrace();
                                return null;
                            }
                        }

                    } catch (SQLException e2) {
                        e2.printStackTrace();
                        return null;
                    }

                } catch (Exception e) {
                    e.printStackTrace();
                    return null;
                }
            }

        } catch (SQLException e2) {
            e2.printStackTrace();
            return null;
        }

        return null;
    }

    /**
     * Überprüft das neugesetze Start und Enddatum der neuen Wiederholung
     *
     * @param start
     * @param ende
     * @param kalender_id
     * @param eintrag_id
     * @param wiederholung
     * @return
     */
    public boolean check_datum_available_at_update_wh(Timestamp start, Timestamp ende, int kalender_id, int eintrag_id, int wiederholung) {
        Connection con = DBConnection.connection();
        boolean result = false;

        try {
            Statement stmt = con.createStatement();

            System.out.println("Übergebene Eintrag_ID: " + eintrag_id);

            ResultSet rs = stmt.executeQuery(

            "SELECT COUNT(*) AS count FROM Kalendereintrag INNER JOIN Teilnahme ON Kalendereintrag.eintrag_id = Teilnahme.eintrag_id " +
                    "WHERE kalender_id=" + kalender_id + " AND wiederholung!=" + wiederholung + " AND ( ('" +
                    start + "' BETWEEN Kalendereintrag.start AND Kalendereintrag.ende ) OR ('" +
                    ende + "' BETWEEN Kalendereintrag.start AND Kalendereintrag.ende) OR ('" +
                    start + "' < Kalendereintrag.start AND '" + ende + "' > Kalendereintrag.ende))");

             while (rs.next()) {
            int ueberschneidungen = rs.getInt("count");
            System.out.println("Errechnete Überschneidungen: " + ueberschneidungen);
            System.out.println("Übergebene Kalender_ID: " + kalender_id);

            if(ueberschneidungen > 0)
            result = true;
        }




        } catch (SQLException e2) {
            e2.printStackTrace();
        }
        return result;



    }

    /**
     * Findet alle Termine für eine Person anhand der übergebenen ID.
     *
     * @param person_id die ID der Person vom Typ int
     * @return Vektor mit allen Terminen
     */
    public Vector <Termin> findall_termine_person(int person_id){
        Connection con = DBConnection.connection();
        Vector result = new Vector();
        Vector <Person> eingeladene_personen = new Vector();

        try {
            Statement stmt = con.createStatement();


            ResultSet rs = stmt.executeQuery(

                    "SELECT * " +
                    "FROM Kalendereintrag " +
                    "WHERE typ='t' AND person_id=" + person_id + " AND wiederholung=0"); //Wiederholung=0 unterscheidet Termine ohne Wiederholungen, bei Wiederholungen mit Terminen sind diese < 0

            while (rs.next()) {
                try {

                    //Pro Eintrag werden nun die Teilnehmer ausgelesen
                    Statement stmt2 = con.createStatement();
                    ResultSet rs2 = stmt2.executeQuery("SELECT person_id " +
                    "FROM Teilnahme " +
                    "WHERE eintrag_id =" + rs.getInt("eintrag_id") + " AND person_id!=" + rs.getInt("person_id"));

                     while (rs2.next()) {
                         try {
                                Statement stmt3 = con.createStatement();
                                ResultSet rs3 = stmt3.executeQuery("SELECT * FROM Person " +
                                "WHERE person_id =" + rs2.getInt("person_id"));

                                    if (rs3.next()) {
                                    try {

                                        PersonImpl pe = new PersonImpl();
                                        pe.set_person_id(rs3.getInt("person_id"));
                                        pe.set_raum(rs3.getInt("raum"));
                                        pe.set_nachname(rs3.getString("nachname"));
                                        pe.set_vorname(rs3.getString("vorname"));
                                        pe.set_telefon(rs3.getInt("telefon"));
                                        pe.set_ist_admin(rs3.getBoolean("ist_admin"));
                                        pe.set_email(rs3.getString("email"));
                                        eingeladene_personen.add(pe);

                                    } catch (Exception e) {
                                        e.printStackTrace();
                                        
                                    }
                                }
                         }catch (Exception e) {
                                        e.printStackTrace();

                                    }
                     }
                    Termin te = new TerminImpl();
                    te.set_beschreibung(rs.getString("beschreibung"));
                    te.set_eintrag_id(rs.getInt("eintrag_id"));
                    te.set_ende(rs.getTimestamp("ende"));
                    te.set_erinnerung(rs.getInt("erinnerung"));
                    te.set_erstelltvon(rs.getInt("person_id"));
                    te.set_kalender_id(0); //Setzen wir hier in dem Fall nicht, da dieser keine Rolle spielt
                    te.set_kategorie(rs.getInt("kategorie_id"));
                    te.set_ort(rs.getString("ort"));
                    te.set_start(rs.getTimestamp("start"));
                    te.set_wiederholung_id(rs.getInt("wiederholung"));
                    te.set_serienmuster(rs.getString("serienmuster").charAt(0));
                    te.set_anzahl(rs.getInt("anzahl"));
                    te.set_eingeladene_personen(eingeladene_personen);
                    result.addElement(te);

                } catch (Exception e) {
                    e.printStackTrace();
                    //Bei einer evtl. Exception wird die while-Schleife abgebrochen.
                    break;
                }

            }
        } catch (SQLException e2) {
            e2.printStackTrace();
        }
        // Ergebnisvektor vom Typ Termin wird zurückgeben.
        return result;

    }

    /**
     * Findet alle Wiederholungen von Terminen für eine Person anhand der übergebenen ID.
     *
     * @param person_id ID der Person vom Typ int
     * @return result Vektor mit den Wiederholungen von Terminen
     */
    public Vector<Termin> findall_wiederholungen_termine_person(int person_id) {
        Connection con = DBConnection.connection();
        Vector <Termin> result = new Vector();     

        try {
            Statement stmt = con.createStatement();


            ResultSet rs = stmt.executeQuery(

                    "SELECT * " +
                    "FROM Kalendereintrag " +
                    "WHERE typ='t' AND person_id=" + person_id + " AND wiederholung!=0 ORDER BY wiederholung ASC" + // Wenn Wiederholung 0 wäre, wäre es ein Kalendereintrag mit Termin
                    "");

            while (rs.next()) {
                try {
                    Termin te = new TerminImpl();
                    te.set_beschreibung(rs.getString("beschreibung"));
                    te.set_eintrag_id(rs.getInt("eintrag_id"));
                    te.set_ende(rs.getTimestamp("ende"));
                    te.set_erinnerung(rs.getInt("erinnerung"));
                    te.set_erstelltvon(rs.getInt("person_id"));
                    te.set_kalender_id(0); //Setzen wir hier in dem Fall nicht, da dieser keine Rolle spielt
                    te.set_kategorie(rs.getInt("kategorie_id"));
                    te.set_ort(rs.getString("ort"));
                    te.set_start(rs.getTimestamp("start"));
                    te.set_serienmuster(rs.getString("serienmuster").charAt(0));
                    te.set_anzahl(rs.getInt("anzahl"));
                    te.set_wiederholung_id(rs.getInt("wiederholung"));

                            //Kalender ID auslesen
                            Statement stmt2 = con.createStatement();
                            ResultSet rs2 = stmt2.executeQuery(

                            "SELECT kalender_id " +
                            "FROM Teilnahme " +
                            "WHERE eintrag_id=" + rs.getInt("eintrag_id") + " AND person_id IS NULL AND teilnahme=1" + // Wenn Wiederholung 0 wäre, wäre es ein Kalendereintrag mit Termin
                            "");

                            if (rs2.next()) {
                                try {
                                    // Hier wird die bisher höhste vorhandene kalender_id um 1 erhöht.
                                     
                                    te.set_kalender_id(rs2.getInt("kalender_id"));
                                    System.out.println("KALENDER ID " + rs2.getInt("kalender_id"));

                                } catch (Exception ex) {
                                    ex.printStackTrace();
                                }
                            }


                   // Wir fügen pro Termin nur EINEN Eintrag dem Vector hinzu
                    if(result.size() > 0)
                        {
                        if(rs.getInt("wiederholung") == result.lastElement().get_wiederholung_id())
                        System.out.println("Termin mit der WiederholungsID: " + rs.getInt("wiederholung") + " ist bereits im Vektor und wird nicht hinzugefügt" );
                        else
                        result.addElement(te);
                        }
                    else
                    {
                        result.addElement(te);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    //Bei einer evtl. Exception wird die while-Schleife abgebrochen.
                    break;
                }
            }
        } catch (SQLException e2) {
            e2.printStackTrace();
        }
        // Ergebnisvektor vom Typ Termin wird zurückgeben
        return result;

    }

    /**
     * Aktualisiert die Wiederholung des Termins.
     *
     * @param editierter_eintrag das zu editierende Termin- Objekt
     */
    public void update_termin_wiederholung(Termin editierter_eintrag) {
       {
        //Stellt durch Aufruf der connection() Methode der DBConnection-Klasse, die Verbindung zur Datenbank her.
        Connection con = DBConnection.connection();

        try {
            // Leeres SQL-Statement stmt wird angelegt.
            Statement stmt = con.createStatement();

                   stmt.executeUpdate("UPDATE Kalendereintrag " +
                    "SET eintrag_id=" + editierter_eintrag.get_eintrag_id() + ", " +
                    "typ='t', " +
                    "erinnerung=" + editierter_eintrag.get_erinnerung() + ", " +
                    "ort='" + editierter_eintrag.get_ort() + "', " +
                    "start='" + editierter_eintrag.get_start() + "', " +
                    "ende='" + editierter_eintrag.get_ende() + "', " +
                    "beschreibung='" + editierter_eintrag.get_beschreibung() + "', " +
                    "wiederholung=" + editierter_eintrag.get_wiederholung_id() + ", " +
                    // "enddatum='null', " +
                    "kategorie_id=" + editierter_eintrag.get_kategorie() + ", " +
                    "person_id=" + editierter_eintrag.get_erstelltvon() + ", " +
                    "anzahl=" + editierter_eintrag.get_anzahl() + ", " +
                    "serienmuster='" + editierter_eintrag.get_serienmuster() + "' " +
                    "WHERE eintrag_id=" + editierter_eintrag.get_eintrag_id());


                   // Nun werden noch alle zugewiesenen Teilnehmer entfernt und neu eingeladen // Der Besitzer nimmt immer teil und wird deswegen NICHT gelöscht
                   stmt.executeUpdate("DELETE FROM Teilnahme " +
                    "WHERE eintrag_id=" + editierter_eintrag.get_eintrag_id() + " AND person_id!=" + editierter_eintrag.get_erstelltvon());

                   // Nun werden alle eingeladenen Teilnehmer Tabelle Teilnehmer geschrieben, diese müssen zuvor aus dem Vector ausgelesen werden
                    for(int i=0; i < editierter_eintrag.get_eingeladene_personen().size(); i++)
                    {
                        stmt.executeUpdate("INSERT INTO Teilnahme (eintrag_id, teilnahme, person_id) VALUES (" +
                            editierter_eintrag.get_eintrag_id() + "," +
                            0 +  "," + // Teilnahme wird auf 0 gesetzt, da die Teilnehmer den Eintrag noch ihrem Kalender hinzufügen müssen
                            editierter_eintrag.get_eingeladene_personen().get(i).get_person_id() + ")");
                    }

        } catch (SQLException e2) {
            e2.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        // das als Parameter übergebende Objekt des Typs Termin wird zurückgegeben
        return;
    }
    }

    /**
     * Löscht alle Daten eines Termin- Objekts aus der Datenbank.
     *
     * @param x das zu löschende Termin- Objekt
     */
    public void delete(Termin x) {
        Connection con = DBConnection.connection();

        try {
            // Leeres SQL-Statement stmt wird angelegt.
            Statement stmt = con.createStatement();

            // Löschen in der Tabelle Kalendereintrag
            stmt.executeUpdate("DELETE FROM Kalendereintrag " +
                    "WHERE eintrag_id=" + x.get_eintrag_id());

            // Löschen in der Tabelle Teilnahme
            stmt.executeUpdate("DELETE FROM Teilnahme " +
                    "WHERE eintrag_id=" + x.get_eintrag_id());

        } catch (SQLException e2) {
            e2.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }


    }

    /**
     * Löscht die Wiederholung für einen Termin
     *
     * @param x der Termin, für den die Wiederholung gelöscht werden soll
     */
    public void delete_wiederholung(Termin x) {
        Connection con = DBConnection.connection();

        try {
            // Leeres SQL-Statement stmt wird angelegt.
            Statement stmt = con.createStatement();

            // Löschen in der Tabelle Teilnahme verknüpft mit INNERJOIN auf Kalendereintrag Tabelle
            stmt.executeUpdate("DELETE Teilnahme.* FROM Teilnahme " +
                    "INNER JOIN Kalendereintrag ON Teilnahme.eintrag_id = Kalendereintrag.eintrag_id " +
                    "WHERE wiederholung=" + x.get_wiederholung_id());

            // Löschen in der Tabelle Kalendereintrag alle Einträge, die die übergebene Wiederholungs ID besitzen
            stmt.executeUpdate("DELETE FROM Kalendereintrag " +
                    "WHERE wiederholung=" + x.get_wiederholung_id());



        } catch (SQLException e2) {
            e2.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
